package com.baomibing.flow.controller;

import com.baomibing.core.common.Assert;
import com.baomibing.core.common.SearchResult;
import com.baomibing.flow.constant.NodeTypeEnum;
import com.baomibing.flow.dto.SysFlowDto;
import com.baomibing.flow.dto.SysFlowInstanceDto;
import com.baomibing.flow.dto.SysFlowTaskDto;
import com.baomibing.flow.enginee.WorkFlowEngine;
import com.baomibing.flow.execute.Execution;
import com.baomibing.flow.model.WorkFlow;
import com.baomibing.flow.service.SysFlowInstanceService;
import com.baomibing.flow.service.SysFlowService;
import com.baomibing.flow.service.SysFlowTaskService;
import com.baomibing.flow.service.WorkFlowEnginee;
import com.baomibing.flow.transfer.UIFlowTransfer;
import com.baomibing.flow.ui.UBaseNode;
import com.baomibing.flow.ui.UWorkFlow;
import com.baomibing.flow.vo.ExecuteVo;
import com.baomibing.flow.vo.StartVo;
import com.baomibing.tool.common.PageQuery;
import com.baomibing.tool.util.Checker;
import com.baomibing.web.base.ActionController;
import com.baomibing.web.common.R;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * FlowController
 *
 * @author frog 2025/3/4 09:50
 * @version 1.0.0
 **/
@Slf4j
@RestController
@RequestMapping(path = { "/api/flow"}, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public class FlowController extends ActionController {

    @Autowired
    private WorkFlowEnginee flowEnginee;
    @Autowired
    private WorkFlowEngine workFlowEngine;
    @Autowired
    private SysFlowService flowService;
    @Autowired
    private SysFlowInstanceService instanceService;
    @Autowired
    private SysFlowTaskService taskService;

    @PostMapping("/search")
    public R<SysFlowDto> search(@RequestBody PageQuery<SysFlowDto>  query) {
        SearchResult<SysFlowDto> result = flowService.search(query.getDto(), query.getPageNo(), query.getPageSize());
        return R.build(result.getTotalSize(), query.getPageNo(), query.getPageSize(), result.getDataList());
    }

    @PostMapping("/searchActive")
    public R<SysFlowDto> searchActive(@RequestBody PageQuery<SysFlowDto>  query) {
        SearchResult<SysFlowDto> result = flowService.searchAllActive(query.getDto(), query.getPageNo(), query.getPageSize());
        return R.build(result.getTotalSize(), query.getPageNo(), query.getPageSize(), result.getDataList());
    }

    @GetMapping("/getInstanceChart")
    public SysFlowInstanceDto getInstanceChart(@RequestParam String id) {
        SysFlowInstanceDto instance = instanceService.getInstance(id);
        WorkFlow workFlow = workFlowEngine.loadFlow(id);
        UWorkFlow uiflow =  UIFlowTransfer.transfer2UWorkFlow(workFlow);
        List<SysFlowTaskDto> tasks = taskService.listExecutingTaskByInstance(id);
        Set<String> nodeIds = tasks.stream().map(SysFlowTaskDto::getNodeId).collect(Collectors.toSet());
        for (UBaseNode node : uiflow.getNodes()) {
            if (nodeIds.contains(node.getId())) {
                node.getData().setRunning(true);
            }
        }
        instance.setData(uiflow);
        return instance;
    }

    @PostMapping("/searchInstanceByFlow")
    public R<SysFlowInstanceDto> searchInstanceByFlow(@RequestBody PageQuery<SysFlowInstanceDto> query) {
        SearchResult<SysFlowInstanceDto> result =  instanceService.searchByFlow(query.getDto(), query.getPageNo(), query.getPageSize());
        return R.build(result.getTotalSize(), query.getPageNo(), query.getPageSize(), result.getDataList());
    }

    @GetMapping("/getFlowChart")
    public SysFlowDto getFlowChart(@RequestParam String id) {
        SysFlowDto flow = flowService.getFlow(id);
        WorkFlow workFlow = workFlowEngine.xflow2WorkFlow(flow);
        UWorkFlow uiflow =  UIFlowTransfer.transfer2UWorkFlow(workFlow);
        flow.setData(uiflow);
        return flow;
    }

    @PostMapping
    public SysFlowDto save(@RequestBody SysFlowDto flow) {
        if (Checker.beNotNull(flow.getData())) {
            WorkFlow deployFlow = UIFlowTransfer.transfer2WorkFlow(flow.getData());
            SysFlowDto dflow = flowEnginee.deploy(deployFlow);
            flow.setNodes(dflow.getNodes()).setStartNode(dflow.getStartNode()).setEndNode(dflow.getEndNode())
                    .setViewPort(dflow.getViewPort()).setStyle(dflow.getStyle()).setProcesses(dflow.getProcesses());
        }
        flowService.saveFlow(flow);
        return flow;
    }

    @PutMapping
    public SysFlowDto upate(@RequestBody SysFlowDto flow) {
        if (Checker.beNotNull(flow.getData())) {
            WorkFlow deployFlow = UIFlowTransfer.transfer2WorkFlow(flow.getData());
            SysFlowDto dflow = flowEnginee.deploy(deployFlow);
            flow.setNodes(dflow.getNodes()).setStartNode(dflow.getStartNode()).setEndNode(dflow.getEndNode())
                    .setViewPort(dflow.getViewPort()).setStyle(dflow.getStyle()).setProcesses(dflow.getProcesses());
        }
        flowService.updateFlow(flow);
        return flow;
    }

    @GetMapping("/{id}")
    public SysFlowDto get(@PathVariable String id) {
        return flowService.getFlow(id);
    }

    @DeleteMapping
    public void delete(@RequestBody List<String> ids) {
        flowService.deleteFlows(Sets.newHashSet(ids));
    }

    @PostMapping("use")
    public void use(@RequestBody List<String> ids) {
        flowService.activeFlows(Sets.newHashSet(ids));
    }

    @PostMapping("copy")
    public void copy(@RequestBody SysFlowDto flow) {
        flowService.doCopy(flow);
    }

    @PostMapping("stop")
    public void stop(@RequestBody List<String> ids) {
        flowService.stopFlows(Sets.newHashSet(ids));
    }

    @GetMapping("listExecutingTaskByInstance")
    public List<SysFlowTaskDto> listExecutingTaskByInstance(@RequestParam String instanceId) {
        return taskService.listExecutingTaskByInstance(instanceId);
    }

    @PostMapping("start")
    public void start(@RequestBody StartVo start) {
        Assert.CheckArgument(start, start.getFlowId());
        if (Checker.beEmpty(start.getActor())) {
            start.setActor(currentUserCnName());
        }
//        Assert.CheckArgument(start, start.getFlowId(), start.getActor());
        flowEnginee.doStartFlow(start.getFlowId(), Execution.createByNodeType(NodeTypeEnum.Start).setActor(start.getActor()));
    }
    @PostMapping(value = "execute", consumes = "multipart/*", headers = "content-Type=multipart/form-data")
    public void doExecute(@ModelAttribute ExecuteVo execute) {
        Map<String, Execution> exeMap = Maps.newHashMap();
        NodeTypeEnum type = Checker.beEmpty(execute.getType()) ? NodeTypeEnum.Task : NodeTypeEnum.valueOf(execute.getType());
        exeMap.put(execute.getName(), Execution.createByNodeType(type)
                .setActor(execute.getActor()).setVariables(execute.getVariables()));
        WorkFlow flow = flowEnginee.doExecute(execute.getInstanceId(), exeMap);
        log.info("==================current=============");
        flow.printCurrent();
        log.info("==================execute task=============");
        flow.printExecutedTasks();
        log.info("==================tracks=============");
        flow.printTracks();
    }

    @PostMapping(value = "reject", consumes = "multipart/*", headers = "content-Type=multipart/form-data")
    public void doReject(@ModelAttribute  ExecuteVo execute) {
        Map<String, Execution> exeMap = Maps.newHashMap();
        NodeTypeEnum type = Checker.beEmpty(execute.getType()) ? NodeTypeEnum.Task : NodeTypeEnum.valueOf(execute.getType());
        exeMap.put(execute.getName(), Execution.createByNodeType(type)
                .setActor(execute.getActor()).setVariables(execute.getVariables()));
        WorkFlow flow = flowEnginee.doReject(execute.getInstanceId(), exeMap);
        log.info("==================current=============");
        flow.printCurrent();
        log.info("==================execute task=============");
        flow.printExecutedTasks();
        log.info("==================tracks=============");
        flow.printTracks();

    }

//    public List<SysFlowTaskDto>

}
